package astiav

//#include "codec_context.h"
import "C"
import (
	"errors"
	"fmt"
	"sync"
	"unsafe"
)

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html
type CodecContext struct {
	c *C.AVCodecContext
}

func newCodecContextFromC(c *C.AVCodecContext) *CodecContext {
	if c == nil {
		return nil
	}
	cc := &CodecContext{c: c}
	classers.set(cc)
	return cc
}

var _ Classer = (*CodecContext)(nil)

// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#gae80afec6f26df6607eaacf39b561c315
func AllocCodecContext(c *Codec) *CodecContext {
	var cc *C.AVCodec
	if c != nil {
		cc = c.c
	}
	return newCodecContextFromC(C.avcodec_alloc_context3(cc))
}

// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#gaf869d0829ed607cec3a4a02a1c7026b3
func (cc *CodecContext) Free() {
	// 完全按照FFmpeg C代码：if (!avctx) return;
	if cc == nil || cc.c == nil {
		return
	}
	
	if cc.c.hw_device_ctx != nil {
		C.av_buffer_unref(&cc.c.hw_device_ctx)
	}
	if cc.c.hw_frames_ctx != nil {
		C.av_buffer_unref(&cc.c.hw_frames_ctx)
	}
	
	// Make sure to clone the classer before freeing the object since
	// the C free method may reset the pointer
	c := newClonedClasser(cc)
	
	// 完全按照FFmpeg C代码：avcodec_free_context会调用av_freep将指针设为NULL
	// 我们需要模拟这个行为
	C.avcodec_free_context(&cc.c)
	// avcodec_free_context内部会调用av_freep，将cc.c设为NULL
	// 但由于CGO的限制，我们需要手动设置
	cc.c = nil
	
	// Make sure to remove from classers after freeing the object since
	// the C free method may use methods needing the classer
	if c != nil {
		classers.del(c)
	}
}

func (cc *CodecContext) String() string {
	s, _ := stringFromC(255, func(buf *C.char, size C.size_t) error {
		C.avcodec_string(buf, C.int(size), cc.c, C.int(0))
		return nil
	})
	return s
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a6b53fda85ad61baa345edbd96cb8a33c
func (cc *CodecContext) BitRate() int64 {
	return int64(cc.c.bit_rate)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a6b53fda85ad61baa345edbd96cb8a33c
func (cc *CodecContext) SetBitRate(bitRate int64) {
	cc.c.bit_rate = C.int64_t(bitRate)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a167ff73c67960acf2d5ca73d93e13f64
func (cc *CodecContext) ChannelLayout() ChannelLayout {
	l, _ := newChannelLayoutFromC(&cc.c.ch_layout).clone()
	return l
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a167ff73c67960acf2d5ca73d93e13f64
func (cc *CodecContext) SetChannelLayout(l ChannelLayout) {
	l.copy(&cc.c.ch_layout) //nolint: errcheck
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#ac60a0209642b5d74068cab0ac35a78b2
func (cc *CodecContext) ChromaLocation() ChromaLocation {
	return ChromaLocation(cc.c.chroma_sample_location)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a90622d3af2a9abba986a1c9f7ca21b16
func (cc *CodecContext) Class() *Class {
	if cc.c == nil {
		return nil
	}
	return newClassFromC(unsafe.Pointer(cc.c))
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#adc5f65d6099fd8339c1580c091777223
func (cc *CodecContext) CodecID() CodecID {
	return CodecID(cc.c.codec_id)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#adc5f65d6099fd8339c1580c091777223
func (cc *CodecContext) SetCodecId(id CodecID) {
	cc.c.codec_id = C.enum_AVCodecID(id)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a8b8b0b8b8b8b8b8b8b8b8b8b8b8b8b8b
func (cc *CodecContext) Codec() *Codec {
	return newCodecFromC(cc.c.codec)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3a41b3e5bde23b877799f6e72dac8ef3
func (cc *CodecContext) ColorPrimaries() ColorPrimaries {
	return ColorPrimaries(cc.c.color_primaries)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3a41b3e5bde23b877799f6e72dac8ef3
func (cc *CodecContext) SetColorPrimaries(p ColorPrimaries) {
	cc.c.color_primaries = C.enum_AVColorPrimaries(p)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a255bf7100a4ba6dcb6ee5d87740a4f35
func (cc *CodecContext) ColorRange() ColorRange {
	return ColorRange(cc.c.color_range)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a255bf7100a4ba6dcb6ee5d87740a4f35
func (cc *CodecContext) SetColorRange(r ColorRange) {
	cc.c.color_range = C.enum_AVColorRange(r)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a8cd8caa7d40319324ce3d879a2edbd9f
func (cc *CodecContext) ColorSpace() ColorSpace {
	return ColorSpace(cc.c.colorspace)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a8cd8caa7d40319324ce3d879a2edbd9f
func (cc *CodecContext) SetColorSpace(s ColorSpace) {
	cc.c.colorspace = C.enum_AVColorSpace(s)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#ab649e8c599f5a0e2a30448e67a36deb6
func (cc *CodecContext) ColorTransferCharacteristic() ColorTransferCharacteristic {
	return ColorTransferCharacteristic(cc.c.color_trc)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#ab649e8c599f5a0e2a30448e67a36deb6
func (cc *CodecContext) SetColorTransferCharacteristic(tc ColorTransferCharacteristic) {
	cc.c.color_trc = C.enum_AVColorTransferCharacteristic(tc)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#abe964316aaaa61967b012efdcced79c4
func (cc *CodecContext) ExtraData() []byte {
	return bytesFromC(func(size *C.size_t) *C.uint8_t {
		*size = C.size_t(cc.c.extradata_size)
		return cc.c.extradata
	})
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#abe964316aaaa61967b012efdcced79c4
func (cc *CodecContext) SetExtraData(b []byte) error {
	return setBytesWithIntSizeInC(b, &cc.c.extradata, &cc.c.extradata_size)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#abb01e291550fa3fb96188af4d494587e
func (cc *CodecContext) Flags() CodecContextFlags {
	return CodecContextFlags(cc.c.flags)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#abb01e291550fa3fb96188af4d494587e
func (cc *CodecContext) SetFlags(fs CodecContextFlags) {
	cc.c.flags = C.int(fs)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a1944f9a4f8f2e123c087e1fe7613d571
func (cc *CodecContext) Flags2() CodecContextFlags2 {
	return CodecContextFlags2(cc.c.flags2)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a1944f9a4f8f2e123c087e1fe7613d571
func (cc *CodecContext) SetFlags2(fs CodecContextFlags2) {
	cc.c.flags2 = C.int(fs)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a4d08b297e97eefd66c714df4fff493c8
func (cc *CodecContext) Framerate() Rational {
	return newRationalFromC(cc.c.framerate)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a4d08b297e97eefd66c714df4fff493c8
func (cc *CodecContext) SetFramerate(f Rational) {
	cc.c.framerate = f.c
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#aec57f0d859a6df8b479cd93ca3a44a33
func (cc *CodecContext) FrameSize() int {
	return int(cc.c.frame_size)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a9b6b3f1fcbdcc2ad9f4dbb4370496e38
func (cc *CodecContext) GopSize() int {
	return int(cc.c.gop_size)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a9b6b3f1fcbdcc2ad9f4dbb4370496e38
func (cc *CodecContext) SetGopSize(gopSize int) {
	cc.c.gop_size = C.int(gopSize)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a0449afd803eb107bd4dbc8b5ea22e363
func (cc *CodecContext) Height() int {
	return int(cc.c.height)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a0449afd803eb107bd4dbc8b5ea22e363
func (cc *CodecContext) SetHeight(height int) {
	cc.c.height = C.int(height)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a6927dc652ae6241f1dfdbad4e12d3a40
func (cc *CodecContext) Level() Level {
	return Level(cc.c.level)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a6927dc652ae6241f1dfdbad4e12d3a40
func (cc *CodecContext) SetLevel(l Level) {
	cc.c.level = C.int(l)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3f99ca3115c44e6d7772c9384faf15e6
func (cc *CodecContext) MediaType() MediaType {
	return MediaType(cc.c.codec_type)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a0425c77b3d06d71e5db88b1d7e1b37f2
func (cc *CodecContext) PixelFormat() PixelFormat {
	return PixelFormat(cc.c.pix_fmt)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a0425c77b3d06d71e5db88b1d7e1b37f2
func (cc *CodecContext) SetPixelFormat(pixFmt PixelFormat) {
	cc.c.pix_fmt = C.enum_AVPixelFormat(pixFmt)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#af3379123060ad8cc9c321c29af4f8360
func (cc *CodecContext) PrivateData() *PrivateData {
	return newPrivateDataFromC(cc.c.priv_data)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a7abe7095de73df98df4895bf9e25fc6b
func (cc *CodecContext) Profile() Profile {
	return Profile(cc.c.profile)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a7abe7095de73df98df4895bf9e25fc6b
func (cc *CodecContext) SetProfile(p Profile) {
	cc.c.profile = C.int(p)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3f63bc9141e25bf7f1cda0cef7cd4a60
func (cc *CodecContext) Qmin() int {
	return int(cc.c.qmin)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3f63bc9141e25bf7f1cda0cef7cd4a60
func (cc *CodecContext) SetQmin(qmin int) {
	cc.c.qmin = C.int(qmin)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a5252d34fbce300228d4dbda19a8c3293
func (cc *CodecContext) SampleAspectRatio() Rational {
	return newRationalFromC(cc.c.sample_aspect_ratio)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a5252d34fbce300228d4dbda19a8c3293
func (cc *CodecContext) SetSampleAspectRatio(r Rational) {
	cc.c.sample_aspect_ratio = r.c
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a1bdba69ea111e2a9d03fdaa7a46a4c45
func (cc *CodecContext) SampleFormat() SampleFormat {
	return SampleFormat(cc.c.sample_fmt)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a1bdba69ea111e2a9d03fdaa7a46a4c45
func (cc *CodecContext) SetSampleFormat(f SampleFormat) {
	cc.c.sample_fmt = C.enum_AVSampleFormat(f)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a8ff0b000c463361e234af48d03aadfc0
func (cc *CodecContext) SampleRate() int {
	return int(cc.c.sample_rate)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a8ff0b000c463361e234af48d03aadfc0
func (cc *CodecContext) SetSampleRate(sampleRate int) {
	cc.c.sample_rate = C.int(sampleRate)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3090804569341ca235e3adbdc03318d2
func (cc *CodecContext) StrictStdCompliance() StrictStdCompliance {
	return StrictStdCompliance(cc.c.strict_std_compliance)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3090804569341ca235e3adbdc03318d2
func (cc *CodecContext) SetStrictStdCompliance(c StrictStdCompliance) {
	cc.c.strict_std_compliance = C.int(c)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#ab7bfeb9fa5840aac090e2b0bd0ef7589
func (cc *CodecContext) TimeBase() Rational {
	return newRationalFromC(cc.c.time_base)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#ab7bfeb9fa5840aac090e2b0bd0ef7589
func (cc *CodecContext) SetTimeBase(r Rational) {
	cc.c.time_base = r.c
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a542
func (cc *CodecContext) PktTimebase() Rational {
	return newRationalFromC(cc.c.pkt_timebase)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a542
func (cc *CodecContext) SetPktTimebase(r Rational) {
	cc.c.pkt_timebase = r.c
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#aa852b6227d0778b62e9cc4034ad3720c
func (cc *CodecContext) ThreadCount() int {
	return int(cc.c.thread_count)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#aa852b6227d0778b62e9cc4034ad3720c
func (cc *CodecContext) SetThreadCount(threadCount int) {
	cc.c.thread_count = C.int(threadCount)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a7651614f4309122981d70e06a4b42fcb
func (cc *CodecContext) ThreadType() ThreadType {
	return ThreadType(cc.c.thread_type)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a7651614f4309122981d70e06a4b42fcb
func (cc *CodecContext) SetThreadType(t ThreadType) {
	cc.c.thread_type = C.int(t)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a0d8f46461754e8abea0847dcbc41b956
func (cc *CodecContext) Width() int {
	return int(cc.c.width)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a0d8f46461754e8abea0847dcbc41b956
func (cc *CodecContext) SetWidth(width int) {
	cc.c.width = C.int(width)
}

// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga11f785a188d7d9df71621001465b0f1d
func (cc *CodecContext) Open(c *Codec, d *Dictionary) error {
	var dc **C.AVDictionary
	if d != nil {
		dc = &d.c
	}
	return newError(C.avcodec_open2(cc.c, c.c, dc))
}

// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga11f785a188d7d9df71621001465b0f1d
func (cc *CodecContext) IsOpen() bool {
	return C.astiavCodecIsOpen(cc.c) != 0
}

// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga11f785a188d7d9df71621001465b0f1d
func (cc *CodecContext) SetCodecID(id CodecID) {
	cc.c.codec_id = C.enum_AVCodecID(id)
}

// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga11f785a188d7d9df71621001465b0f1d
func (cc *CodecContext) GetSupportedConfig(codec *Codec, config CodecConfig, flags uint) (interface{}, error) {
	var outConfigs unsafe.Pointer
	var outNumConfigs C.int
	
	var codecPtr *C.AVCodec
	if codec != nil {
		codecPtr = codec.c
	}
	
	err := newError(C.astiavCodecGetSupportedConfig(cc.c, codecPtr, C.int(config), C.uint(flags), &outConfigs, &outNumConfigs))
	if err != nil {
		return nil, err
	}
	
	if outConfigs == nil {
		return nil, nil // All values are supported
	}
	
	// 根据config类型解析返回的数据
	switch config {
	case CodecConfigPixelFormat:
		return parsePixelFormats(outConfigs, int(outNumConfigs)), nil
	case CodecConfigSampleFormat:
		return parseSampleFormats(outConfigs, int(outNumConfigs)), nil
	case CodecConfigSampleRate:
		return parseSampleRates(outConfigs, int(outNumConfigs)), nil
	case CodecConfigChannelLayout:
		return parseChannelLayouts(outConfigs, int(outNumConfigs)), nil
	case CodecConfigFrameRate:
		return parseFrameRates(outConfigs, int(outNumConfigs)), nil
	case CodecConfigColorRange:
		return parseColorRanges(outConfigs, int(outNumConfigs)), nil
	case CodecConfigColorSpace:
		return parseColorSpaces(outConfigs, int(outNumConfigs)), nil
	default:
		return nil, fmt.Errorf("unsupported config type: %d", config)
	}
}

// https://ffmpeg.org/doxygen/8.0/group__lavc__decoding.html#ga5b8eff59cf259747cf0b31563e38ded6
func (cc *CodecContext) ReceivePacket(p *Packet) error {
	var pc *C.AVPacket
	if p != nil {
		pc = p.c
	}
	return newError(C.avcodec_receive_packet(cc.c, pc))
}

// https://ffmpeg.org/doxygen/8.0/group__lavc__decoding.html#ga58bc4bf1e0ac59e27362597e467efff3
func (cc *CodecContext) SendPacket(p *Packet) error {
	var pc *C.AVPacket
	if p != nil {
		pc = p.c
	}
	return newError(C.avcodec_send_packet(cc.c, pc))
}

// https://ffmpeg.org/doxygen/8.0/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c
func (cc *CodecContext) ReceiveFrame(f *Frame) error {
	var fc *C.AVFrame
	if f != nil {
		fc = f.c
	}
	return newError(C.avcodec_receive_frame(cc.c, fc))
}

// https://ffmpeg.org/doxygen/8.0/group__lavc__decoding.html#ga9395cb802a5febf1f00df31497779169
func (cc *CodecContext) SendFrame(f *Frame) error {
	var fc *C.AVFrame
	if f != nil {
		fc = f.c
	}
	return newError(C.avcodec_send_frame(cc.c, fc))
}

func (cc *CodecContext) ToCodecParameters(cp *CodecParameters) error {
	return cp.FromCodecContext(cc)
}

func (cc *CodecContext) FromCodecParameters(cp *CodecParameters) error {
	return cp.ToCodecContext(cc)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#acf8113e490f9e7b57465e65af9c0c75c
func (cc *CodecContext) SetHardwareDeviceContext(hdc *HardwareDeviceContext) {
	if cc.c.hw_device_ctx != nil {
		C.av_buffer_unref(&cc.c.hw_device_ctx)
	}
	if hdc != nil {
		cc.c.hw_device_ctx = C.av_buffer_ref(hdc.c)
	} else {
		cc.c.hw_device_ctx = nil
	}
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3bac44bb0b016ab838780cc19ac277d6
func (cc *CodecContext) HardwareFramesContext() *HardwareFramesContext {
	return newHardwareFramesContextFromC(cc.c.hw_frames_ctx)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3bac44bb0b016ab838780cc19ac277d6
func (cc *CodecContext) SetHardwareFramesContext(hfc *HardwareFramesContext) {
	if cc.c.hw_frames_ctx != nil {
		C.av_buffer_unref(&cc.c.hw_frames_ctx)
	}
	if hfc != nil {
		cc.c.hw_frames_ctx = C.av_buffer_ref(hfc.c)
	} else {
		cc.c.hw_frames_ctx = nil
	}
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#ad2f772bd948d8f3be4d674a3a52ee00e
func (cc *CodecContext) ExtraHardwareFrames() int {
	return int(cc.c.extra_hw_frames)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#ad2f772bd948d8f3be4d674a3a52ee00e
func (cc *CodecContext) SetExtraHardwareFrames(n int) {
	cc.c.extra_hw_frames = C.int(n)
}

func (cc *CodecContext) UnsafePointer() unsafe.Pointer {
	return unsafe.Pointer(cc.c)
}

type CodecContextPixelFormatCallback func(pfs []PixelFormat) PixelFormat

var (
	codecContextPixelFormatCallbacks      = make(map[*C.AVCodecContext]CodecContextPixelFormatCallback)
	codecContextPixelFormatCallbacksMutex = &sync.Mutex{}
)

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a360a2b8508a67c4234d97f4c13ba1bb5
func (cc *CodecContext) SetPixelFormatCallback(c CodecContextPixelFormatCallback) {
	// Lock
	codecContextPixelFormatCallbacksMutex.Lock()
	defer codecContextPixelFormatCallbacksMutex.Unlock()

	// Update callback
	if c == nil {
		C.astiavResetCodecContextGetFormat(cc.c)
		delete(codecContextPixelFormatCallbacks, cc.c)
	} else {
		C.astiavSetCodecContextGetFormat(cc.c)
		codecContextPixelFormatCallbacks[cc.c] = c
	}
}

//export goAstiavCodecContextGetFormat
func goAstiavCodecContextGetFormat(cc *C.AVCodecContext, pfsCPtr *C.enum_AVPixelFormat, pfsCSize C.int) C.enum_AVPixelFormat {
	// Lock
	codecContextPixelFormatCallbacksMutex.Lock()
	defer codecContextPixelFormatCallbacksMutex.Unlock()

	// Get callback
	c, ok := codecContextPixelFormatCallbacks[cc]
	if !ok {
		return C.enum_AVPixelFormat(PixelFormatNone)
	}

	// Get pixel formats
	var pfs []PixelFormat
	for _, v := range unsafe.Slice(pfsCPtr, pfsCSize) {
		pfs = append(pfs, PixelFormat(v))
	}

	// Callback
	return C.enum_AVPixelFormat(c(pfs))
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3e5334a611a3e2a6a653805bb9e2d4d4
func (cc *CodecContext) MaxBFrames() int {
	return int(cc.c.max_b_frames)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a3e5334a611a3e2a6a653805bb9e2d4d4
func (cc *CodecContext) SetMaxBFrames(n int) {
	cc.c.max_b_frames = C.int(n)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#aa2b5582f1a360534310b686cc3f7c668
func (cc *CodecContext) RateControlMaxRate() int64 {
	return int64(cc.c.rc_max_rate)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#aa2b5582f1a360534310b686cc3f7c668
func (cc *CodecContext) SetRateControlMaxRate(n int64) {
	cc.c.rc_max_rate = C.int64_t(n)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#ac265c70b89e87455ec05eb2978def81b
func (cc *CodecContext) RateControlMinRate() int64 {
	return int64(cc.c.rc_min_rate)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#ac265c70b89e87455ec05eb2978def81b
func (cc *CodecContext) SetRateControlMinRate(n int64) {
	cc.c.rc_min_rate = C.int64_t(n)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a15000607a7e2371162348bb35b0184c1
func (cc *CodecContext) RateControlBufferSize() int {
	return int(cc.c.rc_buffer_size)
}

// https://ffmpeg.org/doxygen/8.0/structAVCodecContext.html#a15000607a7e2371162348bb35b0184c1
func (cc *CodecContext) SetRateControlBufferSize(n int) {
	cc.c.rc_buffer_size = C.int(n)
}

// AlignDimensions aligns video dimensions for optimal performance
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga0c7058f764778615e7978a1821ab3cfe
func (cc *CodecContext) AlignDimensions(width, height *int) {
	var w, h C.int = C.int(*width), C.int(*height)
	C.avcodec_align_dimensions(cc.c, &w, &h)
	*width = int(w)
	*height = int(h)
}

// AlignDimensions2 aligns video dimensions with custom line size alignment
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga4b33e6e7c9e9b2d8b1b8c8f8f8f8f8f8
func (cc *CodecContext) AlignDimensions2(width, height *int, linesize []int) {
	var w, h C.int = C.int(*width), C.int(*height)
	var linesizeArray [4]C.int
	
	// Convert Go slice to C array
	for i := 0; i < len(linesize) && i < 4; i++ {
		linesizeArray[i] = C.int(linesize[i])
	}
	
	C.avcodec_align_dimensions2(cc.c, &w, &h, &linesizeArray[0])
	*width = int(w)
	*height = int(h)
	
	// Update linesize slice
	for i := 0; i < len(linesize) && i < 4; i++ {
		linesize[i] = int(linesizeArray[i])
	}
}

// GetHwFramesParameters gets hardware frames parameters
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func (cc *CodecContext) GetHwFramesParameters(deviceRef *HardwareDeviceContext) (*HardwareFramesContext, error) {
	if cc.c == nil {
		return nil, errors.New("codec context is nil")
	}
	
	var hwFramesRef *C.AVBufferRef
	var deviceRefC *C.AVBufferRef
	if deviceRef != nil {
		deviceRefC = deviceRef.c
	}
	
	ret := C.avcodec_get_hw_frames_parameters(cc.c, deviceRefC, cc.c.pix_fmt, &hwFramesRef)
	if ret < 0 {
		return nil, newError(ret)
	}
	
	if hwFramesRef == nil {
		return nil, errors.New("failed to get hardware frames parameters")
	}
	
	return newHardwareFramesContextFromC(hwFramesRef), nil
}

// DefaultGetBuffer2 is the default callback for getting frame buffers
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga9b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func (cc *CodecContext) DefaultGetBuffer2(frame *Frame, flags int) error {
	if cc.c == nil {
		return errors.New("codec context is nil")
	}
	if frame == nil || frame.c == nil {
		return errors.New("frame is nil")
	}
	
	ret := C.avcodec_default_get_buffer2(cc.c, frame.c, C.int(flags))
	if ret < 0 {
		return newError(ret)
	}
	return nil
}



// DefaultExecute is the default execute callback
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func (cc *CodecContext) DefaultExecute(fn unsafe.Pointer, arg unsafe.Pointer, ret *int, count int, size int) int {
	if cc.c == nil {
		return -1
	}
	
	var retC C.int
	result := C.avcodec_default_execute(cc.c, (*[0]byte)(fn), arg, &retC, C.int(count), C.int(size))
	if ret != nil {
		*ret = int(retC)
	}
	
	return int(result)
}

// DefaultExecute2 is the advanced default execute callback
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga9b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func (cc *CodecContext) DefaultExecute2(fn unsafe.Pointer, arg unsafe.Pointer, ret *int, count int) int {
	if cc.c == nil {
		return -1
	}
	
	var retC C.int
	result := C.avcodec_default_execute2(cc.c, (*[0]byte)(fn), arg, &retC, C.int(count))
	if ret != nil {
		*ret = int(retC)
	}
	
	return int(result)
}

// DefaultGetEncodeBuffer is the default callback for getting encode buffers
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga7b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func (cc *CodecContext) DefaultGetEncodeBuffer(pkt *Packet, flags int) error {
	if cc.c == nil {
		return errors.New("codec context is nil")
	}
	if pkt == nil || pkt.c == nil {
		return errors.New("packet is nil")
	}
	
	ret := C.avcodec_default_get_encode_buffer(cc.c, pkt.c, C.int(flags))
	if ret < 0 {
		return newError(ret)
	}
	return nil
}

// FlushBuffers flushes buffers, should be called when seeking or when switching to a different stream
// https://ffmpeg.org/doxygen/8.0/group__lavc__decoding.html#ga7b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func (cc *CodecContext) FlushBuffers() {
	if cc.c != nil {
		C.avcodec_flush_buffers(cc.c)
	}
}

// CodecString returns a string describing the codec context
// This function generates a human-readable string describing the codec context
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga503f8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func (cc *CodecContext) CodecString(encode bool) string {
	if cc.c == nil {
		return ""
	}
	
	buf := make([]byte, 1024)
	encodeFlag := 0
	if encode {
		encodeFlag = 1
	}
	
	C.avcodec_string((*C.char)(unsafe.Pointer(&buf[0])), C.int(len(buf)), cc.c, C.int(encodeFlag))
	
	// Find the null terminator
	for i, b := range buf {
		if b == 0 {
			return string(buf[:i])
		}
	}
	return string(buf)
}

// GetAudioFrameDuration2 gets the duration of an audio frame from codec parameters
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga7b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func GetAudioFrameDuration2(par *CodecParameters, frameBytes int) int {
	if par == nil || par.c == nil {
		return 0
	}
	return int(C.av_get_audio_frame_duration2(par.c, C.int(frameBytes)))
}

// GetAudioFrameDuration gets the duration of an audio frame
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga7b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func (cc *CodecContext) GetAudioFrameDuration(frameBytes int) int {
	if cc.c == nil {
		return 0
	}
	return int(C.av_get_audio_frame_duration(cc.c, C.int(frameBytes)))
}

// FastPaddedMalloc allocates a buffer with padding, optimized for speed
// This function allocates a buffer with AV_INPUT_BUFFER_PADDING_SIZE additional bytes
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga7b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func FastPaddedMalloc(ptr unsafe.Pointer, size *uint, minSize int) {
	C.av_fast_padded_malloc(ptr, (*C.uint)(unsafe.Pointer(size)), C.size_t(minSize))
}

// FastPaddedMallocz allocates a zero-initialized buffer with padding, optimized for speed
// This function allocates a buffer with AV_INPUT_BUFFER_PADDING_SIZE additional bytes and zeros it
// https://ffmpeg.org/doxygen/8.0/group__lavc__core.html#ga7b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b
func FastPaddedMallocz(ptr unsafe.Pointer, size *uint, minSize int) {
	C.av_fast_padded_mallocz(ptr, (*C.uint)(unsafe.Pointer(size)), C.size_t(minSize))
}
